home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
Bitmap Libraries 2.0
/
Examples
/
PaintBucket
/
PaintBucket.c
< prev
next >
Wrap
Text File
|
1996-02-27
|
11KB
|
450 lines
/* File Example.c Copyright (C) 1996 by John R. Montbriand. All Rights Reserved. */
/* File Example.c
Copyright (C) 1996 by John Montbriand. All Rights Reserved.
Distribute freely in areas where the laws of copyright apply.
Use at your own risk.
Do not distribute modified copies.
These various BitMap libraries and examples are for free!
See the accompanying file BitMap.txt for details.
*/
/* written in Geneva 9, tabs at 4 */
/* •••••••••••••••••••••• INCLUDES •••••••••••••••••••••• */
#include <Types.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Windows.h>
#include <Menus.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <AppleEvents.h>
#include <TextUtils.h>
#include <OSUtils.h>
#include <ToolUtils.h>
#include <Desk.h>
#include <Memory.h>
#include <Errors.h>
#include <Gestalt.h>
#include <Traps.h>
#include <SegLoad.h>
#include "BitMap.h"
/* •••••••••••••••••••••• CONSTANTS •••••••••••••••••••••• */
#define kAboutBoxID 128
#define kErrorBoxID 129
#define kMainWindID 128
#define kMenuBarID 128
#define mApple 128
#define iAbout 1
#define iAccessoryOne 3
#define mFile 129
#define iQuit 1
#define mEdit 130
#define iUndo 1
#define iCut 3
#define iCopy 4
#define iPaste 5
#define iClear 6
/* •••••••••••••••••••••• GLOBALS •••••••••••••••••••••• */
Boolean PROGRAM_RUNNING = true;
Boolean PROGRAM_FRONT = true;
Boolean HAS_APPLEEVENTS = false;
Boolean HAS_WAITNEXTEVENT = false;
QDGlobals qd;
DialogPtr gABOUTBOX = NULL;
WindowPtr gMAIN_WINDOW = NULL;
/* •••••••••••••••••••••• ERROR HANDLER •••••••••••••••••••••• */
/* Terminate, displays an error box containing the error number and then terminates
the application. */
void Terminate(OSErr err) {
Str255 s;
NumToString(err, s);
ParamText(s, NULL, NULL, NULL);
Alert(kErrorBoxID, NULL);
ExitToShell();
}
/* •••••••••••••••••••••• THE EXAMPLE PART •••••••••••••••••••••• */
/* NOTE: all of the example code to do with bitmaps is included in this part of the
program. The rest of the program has to do with the usual things that go into
applications. */
Rect gBounds;
CursHandle gPaintCursor;
BitMap *pic_bits, *mask_bits;
/*DrawWindowContents is called in response to every update event for the main
window. this is the part where we do drawing in the bitmaps */
void DrawWindowContents(WindowPtr wp) {
if (mask_bits == NULL) {
/* no mask, just draw the image... */
PlotBitMap(pic_bits, 5, 5, srcCopy);
} else {
BitMap *gray_mask, *drawn_bits;
BitMapPort* bmp;
/* calculate the masks */
gray_mask = NULL;
drawn_bits = NULL;
/* calculate the painted area in gray */
gray_mask = DuplicateBitMap(mask_bits);
if (gray_mask == NULL) goto bail;
WithBitMap(gray_mask, bmp) {
PenMode(patBic);
PenPat(&qd.gray);
PaintRect(&gray_mask->bounds);
}
/* add the painted area to the drawn area */
drawn_bits = BitMapOR(gray_mask, pic_bits);
if (drawn_bits == NULL) goto bail;
/* draw the image */
PlotBitMap(drawn_bits, 5, 5, srcCopy);
bail:
if (gray_mask != NULL) KillBitMap(gray_mask);
if (drawn_bits != NULL) KillBitMap(drawn_bits);
}
}
/*ClickWindowContents is called every time a mouse down event occurs in the
main program window allowing for some interaction. Here, we track the
mouse re-drawing the window appropriately as the mouse is dragged
accross the window*/
void ClickWindowContents(WindowPtr wp, Point where) {
Point location, lastpos;
BitMap* next_mask;
location = where;
SetPt(&lastpos, 0, 0);
do {
if (PtInRect(location, &gBounds) && !EqualPt(lastpos, location)) {
lastpos = location;
/* map the point from the drawn position, to the image */
MapPt(&location, &gBounds, &pic_bits->bounds);
/* make a new mask */
next_mask = PaintBucketBitMap(pic_bits, location.h, location.v);
if (next_mask == NULL) Terminate(memFullErr);
/* redraw the window only if a change occured */
if (!EqualBitMaps(mask_bits, next_mask)) {
/* replace the old mask */
if (mask_bits != NULL)
KillBitMap(mask_bits);
mask_bits = next_mask;
/* redraw the window */
DrawWindowContents(wp);
} else KillBitMap(next_mask);
}
GetMouse(&location);
} while (StillDown());
}
/* InitExample is called at program startup after the main window has been
created to allow the example to set itself up. I put it here so all the example
code would be together in one section. */
OSErr InitExample(WindowPtr wp) {
PicHandle pic;
OSErr err;
/* initial state */
pic_bits = NULL;
mask_bits = NULL;
/* get the resources */
gPaintCursor = GetCursor(128);
if (gPaintCursor == NULL) { err = resNotFound; goto bail; }
pic = GetPicture(129);
if (pic == NULL) { err = resNotFound; goto bail; }
/* make the bitmap */
pic_bits = PICTToBitMap(pic);
if (pic == NULL) { err = memFullErr; goto bail; }
/* adjust the window size */
SizeWindow(wp, pic_bits->bounds.right+10, pic_bits->bounds.bottom+10, true);
gBounds = pic_bits->bounds;
OffsetRect(&gBounds, 5, 5);
/* done */
return noErr;
bail:
if (pic_bits != NULL) KillBitMap(pic_bits);
return err;
}
/* AdjustCursor is called once every time through the main loop so we
can set up the cursor to look like a paint bucket when it's over the
image. */
void AdjustCursor(void) {
if (FrontWindow() == gMAIN_WINDOW) {
Point mouseLoc;
SetPort(gMAIN_WINDOW);
GetMouse(&mouseLoc);
if (PtInRect(mouseLoc, &gBounds)) {
SetCursor(*gPaintCursor);
return;
}
}
InitCursor();
}
/* •••••••••••••••••••••• MENU COMMANDS •••••••••••••••••••••• */
/* HandleMenuCommand is called after MenuKey or MenuSelect to handle the processing
of menu commands. Here there aren't too many, just the standard ones */
void HandleMenuCommand(short menu, short item) {
if (menu == mApple) {
if (item == iAbout) {
if (gABOUTBOX != NULL)
SelectWindow(gABOUTBOX);
else gABOUTBOX = GetNewDialog(kAboutBoxID, NULL, (WindowPtr) (-1));
} else if (item >= iAccessoryOne) {
Str255 itemString;
GetItem(GetMHandle(menu), item, itemString);
OpenDeskAcc(itemString);
}
} else if (menu == mFile) {
switch (item) {
case iQuit:
PROGRAM_RUNNING = false;
break;
}
} else if (menu == mEdit) {
SystemEdit(item-1);
}
HiliteMenu(0);
}
/* MenuPreflightCheck is called immediately before MenuKey or MenuSelect. In your
application you would use this routine to appropriately enable or disable different
commands depending on what can be done at the time of the mouse click or key
command. */
void MenuPreflightCheck(void) {
/* none here*/
}
/* •••••••••••••••••••••• EVENT HANDLING •••••••••••••••••••••• */
/* HandleMouseDownEvent is called to process mouse down events */
void HandleMouseDownEvent(EventRecord *ev) {
WindowPtr target;
switch ( FindWindow(ev->where, &target) ) {
case inDesk:
break;
case inSysWindow:
SystemClick(ev, target);
break;
case inContent:
if (target != FrontWindow())
SelectWindow(target);
else if (target == gMAIN_WINDOW) {
Point where;
where = ev->where;
SetPort(target);
GlobalToLocal(&where);
ClickWindowContents(target, where);
}
break;
case inDrag:
{ Rect r = qd.screenBits.bounds;
if (target != FrontWindow()) SelectWindow(target);
SetPort(target);
InsetRect(&r, 4, 4);
r.top += 20;
DragWindow(target, ev->where, &r);
}
break;
case inGoAway:
if (TrackGoAway(target, ev->where)) {
if (target == gABOUTBOX) {
DisposeDialog(gABOUTBOX);
gABOUTBOX = NULL;
} else PROGRAM_RUNNING = false;
}
break;
case inMenuBar:
{ long mnRes;
MenuPreflightCheck();
if (HiWord(mnRes = MenuSelect(ev->where)) != 0)
HandleMenuCommand(HiWord(mnRes), LoWord(mnRes));
}
break;
}
}
/* SystemSwapTask can be called from anywhere in your application. basically it collects
events and gives the system time to do its thing. */
void SystemSwapTask(void) {
EventRecord ev;
/* get the next event */
if (HAS_WAITNEXTEVENT) {
if (!WaitNextEvent(everyEvent, &ev, 0, NULL)) ev.what = nullEvent;
} else {
if (!GetNextEvent(everyEvent, &ev)) ev.what = nullEvent;
SystemTask();
}
AdjustCursor();
if ((ev.what == keyDown || ev.what == autoKey) && (ev.modifiers & cmdKey) != 0) {
long mnRes;
MenuPreflightCheck();
if (HiWord(mnRes = MenuKey((char) (ev.message & charCodeMask))) != 0)
HandleMenuCommand(HiWord(mnRes), LoWord(mnRes));
} else if (IsDialogEvent(&ev)) {
DialogPtr theDialog;
short itemHit;
DialogSelect(&ev, &theDialog, &itemHit);
} else if (ev.what == mouseDown) {
HandleMouseDownEvent(&ev);
} else if (ev.what == updateEvt) {
WindowPtr wp;
wp = (WindowPtr) ev.message;
SetPort(wp);
BeginUpdate(wp);
DrawWindowContents(wp);
EndUpdate(wp);
} else if (ev.what == kHighLevelEvent) {
if (HAS_APPLEEVENTS)
AEProcessAppleEvent(&ev);
} else if (ev.what == osEvt) {
if (((ev.message >> 24) & 0x0FF) == suspendResumeMessage)
PROGRAM_FRONT = (ev.message & resumeFlag) != 0;
}
}
/* •••••••••••••••••••••• APPLE EVENTS •••••••••••••••••••••• */
/* QuitEventHandler called for quit apple events. all we do here is set the running
flag to false */
pascal OSErr QuitEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) {
DescType retType;
Size actSize;
OSErr err;
err = AEGetAttributePtr(appleEvt, keyMissedKeywordAttr, typeWildCard, &retType, NULL, 0, &actSize);
if (err == errAEDescNotFound) {
PROGRAM_RUNNING = false;
return noErr;
} else if (err == noErr)
return errAEEventNotHandled;
else return err;
}
/* •••••••••••••••••••••• TEST FOR TRAPS •••••••••••••••••••••• */
unsigned short NumToolboxTraps(void) {
if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
return 0x0200;
else return 0x0400;
}
TrapType FindTrapType(unsigned short theTrap) {
if (theTrap & 0x0800)
return ToolTrap;
else return OSTrap;
}
Boolean TrapAvailable(unsigned short theTrap) {
unsigned short localTrap = theTrap;
TrapType tType = FindTrapType(localTrap);
if ( tType == ToolTrap ) {
localTrap &= 0x07FF;
if ( localTrap >= NumToolboxTraps() )
localTrap = _Unimplemented;
}
return NGetTrapAddress(localTrap, tType) != NGetTrapAddress(_Unimplemented, ToolTrap);
}
/* •••••••••••••••••••••• MAIN PROGRAM •••••••••••••••••••••• */
void main(void) {
OSErr err;
long response;
/* determine features of the machine */
if (Gestalt(gestaltAppleEventsAttr, &response) != noErr) response = 0;
HAS_APPLEEVENTS = ((response & (1<<gestaltAppleEventsPresent)) != 0);
HAS_WAITNEXTEVENT = TrapAvailable(_WaitNextEvent);
/* initialize the toolbox, etc.. */
SetApplLimit( GetApplLimit() - (8*1024) );
MaxApplZone();
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
TEInit();
InitMenus();
InitDialogs(NULL);
InitCursor();
if (HAS_APPLEEVENTS) {
err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
NewAEEventHandlerProc(QuitEventHandler), 0, false);
if (err != noErr) goto bail;
}
SetMenuBar(GetNewMBar(kMenuBarID));
AddResMenu(GetMHandle(mApple), 'DRVR');
DrawMenuBar();
gMAIN_WINDOW = GetNewWindow(kMainWindID, NULL, (WindowPtr) (-1));
SetPort(gMAIN_WINDOW);
err = InitExample(gMAIN_WINDOW);
/* set up some globals */
PROGRAM_RUNNING = true;
PROGRAM_FRONT = true;
/* loop until PROGRAM_RUNNING is false */
while (PROGRAM_RUNNING)
SystemSwapTask();
return;
bail:
Terminate(err);
}
/* end File BitMap.c */